package com.shure.queue.utils;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.util.Map;

/**
 * Spring 容器的工具方法 - 手动从 Spring 容器中获取 bean 对象
 *
 * @author Shure
 * @date 2022/1/21 16:54
 */
@Component
public class ApplicationContextUtils implements ApplicationContextAware {


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        statisApplicationContext = applicationContext;
    }

    //@Autowired
    //private ApplicationContext applicationContext;

    private static ApplicationContext statisApplicationContext;

    /**
     * 初始化方法
     */
    //@PostConstruct
    //public void init() {
    //    ApplicationContextUtils.statisApplicationContext = applicationContext;
    //}

    /**
     * 根据class 类型获取 Spring 容器中的对象
     *
     * @param cls
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class<T> cls) {
        if (statisApplicationContext != null) {
            return statisApplicationContext.getBean(cls);
        }
        return null;
    }

    /**
     * 根据class 类型获取 Spring 容器中有某个注解的类对象
     *
     * @param cls
     * @return
     */
    public static Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> cls) {
        if (statisApplicationContext != null) {
            return statisApplicationContext.getBeansWithAnnotation(cls);
        }
        return null;
    }

    /**
     * 主动向Spring容器中注册bean
     *
     * @param beanName BeanName
     * @param registerBeanClazz 将要注册的bean的类
     * @param args 构造方法的必要参数，顺序和类型要求和clazz中定义的一致
     * @return 返回注册到容器中的bean对象
     */
    public static <T> T registerBean(String beanName, Class<T> registerBeanClazz, Object... args) {

        if (statisApplicationContext == null) return null;

        ConfigurableApplicationContext applicationContext = (ConfigurableApplicationContext) statisApplicationContext;

        //容器中是否已经包含此bean
        if (applicationContext.containsBean(beanName)) {
            Object bean = applicationContext.getBean(beanName);

            //的类或接口是否相同，或是否是其超类或超接口。如果是则返回 true；否则返回 false。如果该 Class 表示一个基本类型，且指定的 Class 参数正是该 Class 对象，则该方法返回 true；否则返回 false
            if (bean.getClass().isAssignableFrom(registerBeanClazz)) {
                return (T) bean;
            } else {
                throw new RuntimeException("BeanName 重复 " + beanName);
            }
        }

        //Spring的bean的定义构造器
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(registerBeanClazz);
        //给要注册的bean设置构造函数相应的参数,注意,顺序要相同
        for (Object arg : args) {
            beanDefinitionBuilder.addConstructorArgValue(arg);
        }
        //获取原始的bean
        BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition();
        //获取bean定义注册的工厂
        BeanDefinitionRegistry beanFactory = (BeanDefinitionRegistry) applicationContext.getBeanFactory();
        //开启注册bean到Sring容器中
        beanFactory.registerBeanDefinition(beanName, beanDefinition);
        return applicationContext.getBean(beanName, registerBeanClazz);
    }

    /**
     * 动态注入单例bean实例
     *
     * @param beanName bean名称
     * @param singletonObject 单例bean实例
     * @return 注入实例
     */
    public static Object registerSingletonBean(String beanName, Object singletonObject) {

        //将applicationContext转换为ConfigurableApplicationContext
        ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) statisApplicationContext;

        //获取BeanFactory
        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getAutowireCapableBeanFactory();

        //动态注册bean.
        defaultListableBeanFactory.registerSingleton(beanName, singletonObject);

        //获取动态注册的bean.
        return configurableApplicationContext.getBean(beanName);
    }


}
